/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2011 SMUNIX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
* Author: Providence M. Salumu <providence.salumu@smunix.com>
*/


#ifndef FRAME_HPP__064908
#define FRAME_HPP__064908

#include <type_traits>
#include <slib/aggregator.hpp>

namespace smunix
{
  template<class ...Args>
  class Frame : public smunix::Aggregator<Frame<Args...>, Args... >
  {
    typedef smunix::Aggregator<Frame<Args...>, Args... > BaseAggregator;
  public:
    using BaseAggregator::Ref;
    typedef Frame ThisType;
    template<class XY>
    struct Rebind { typedef Frame<XY> Type; };
  public:
    Frame (void)
    {
      Create ();
      Inject ();
    }
    virtual ~Frame (void)
    {}
  protected:
  private:
    template<class ...FECHArgs>
    struct ForEachCreateHelper;

    template<class A>
    struct ForEachCreateHelper<A>
    {
      typedef std::shared_ptr<A> ASP;
      template<class F>
      static void Apply (F &f)
      {
        if (not f.template PeekWithRef<A> ())
          f.template Pack<A> (Creator<A>::Apply ());
      }
    };

    template<class A, class ...FECHArgs>
    struct ForEachCreateHelper<A, FECHArgs...>
    {
      template<class F>
      static void Apply (F &f)
      {
        ForEachCreateHelper<A>::template Apply (f);
        ForEachCreateHelper<FECHArgs...>::template Apply (f);
      }
    };

    void Create (void)
    {
      ForEachCreateHelper<Args...>::Apply (*this);
    }

    template<class CurrentTp, class IsAggregatedBase, class ...FEOTIH>
    struct ForEachOtherTypeInjectHelper;

    template<class A>
    struct ForEachOtherTypeInjectHelper<A, std::true_type, A>
    {
      typedef std::shared_ptr<A> ASP;
      template<class F>
      static void Apply (ASP &a, F &f)
      {}
    };
    template<class A, class B>
    struct ForEachOtherTypeInjectHelper<A, std::false_type, B>
    {
      typedef std::shared_ptr<A> ASP;
      template<class F>
      static void Apply (ASP &a, F &f)
      {}
    };

    template<class A, class B>
    struct ForEachOtherTypeInjectHelper<A, std::true_type, B>
    {
      typedef std::shared_ptr<A> ASP;
      template<class F>
      static void Apply (ASP &a, F &f)
      {
        if (a)
          a->template VisitAndInject (f.template PeekWithRef<B> ());
      }
    };

    template<class CurrentTp, class IsAggregatedBase, class NextTp, class ...FEOTIH>
    struct ForEachOtherTypeInjectHelper<CurrentTp, IsAggregatedBase, NextTp, FEOTIH...>
    {
      typedef std::shared_ptr<CurrentTp> CurrentTpSP;
      typedef std::shared_ptr<NextTp> NextTpSP;

      template<class F>
      static void Apply (CurrentTpSP &a, F &f)
      {
        ForEachOtherTypeInjectHelper<CurrentTp, IsAggregatedBase, NextTp>::template Apply (a, f);
        ForEachOtherTypeInjectHelper<CurrentTp, IsAggregatedBase, FEOTIH...>::template Apply (a, f);
      }
    };

    template<class ...FECHArgs>
    struct ForEachInjectHelper;

    template<class A>
    struct ForEachInjectHelper<A>
    {
      template<class F>
      static void Apply (F &f)
      {
        ForEachOtherTypeInjectHelper<A, typename std::is_base_of<details::AggregatorBase, A>::type ,Args...>::template Apply (f.template PeekWithRef<A> (), f);
      }
    };

    template<class A, class ...FEIH>
    struct ForEachInjectHelper<A, FEIH...>
    {
      template<class F>
      static void Apply (F &f)
      {
        ForEachInjectHelper<A>::template Apply (f);
        ForEachInjectHelper<FEIH...>::template Apply (f);
      }
    };

    void Inject (void)
    {
      ForEachInjectHelper<Args...>::Apply (*this);
    }
  }; // class Frame
} // namespace smunix

#endif /* FRAME_HPP__064908 */
